<?php

namespace App\Jobs;

use App\User;
use App\UserCoinLog;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Ramsey\Uuid\Uuid;

class GameJbpSeize implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    private $uid;
    private $id;
    private $config;

    //购买每个元宝消耗小妖币
    const yb_cost_xyb = 1;
    //聚宝盆
    const business_id  = 9;
    //元宝类型
    const yb_type = [
        'Y' => ['rate' => 40, 'limit' => 3, 'xyb' => 0.35],//银元宝
        'J' => ['rate' => 10, 'limit' => 5, 'xyb' => 0.25],//金元宝
        'L' => ['rate' => 25, 'limit' => 20, 'xyb' => 0.07],//蓝元宝
        'H' => ['rate' => 25, 'limit' => 100, 'xyb' => 0.02],//红元宝
    ];
    //脚本
    const lua = <<<LUA
local uid = ARGV[1];
local start_ts = tonumber(ARGV[2]);
local now = tonumber(ARGV[3]);
local today = ARGV[4];
local yb_total_num = tonumber(ARGV[5]);
local activity_time = tonumber(ARGV[6]);
local issue_num_key = "jbp:stat:yb:issue:num:" .. today;
local user_limit_key = "jbp:user:" .. uid .. ":limit:num:" .. today;

-- 为用户随机一个今日能抢到的数量（3-50），如果没有则创建，过期时间1小时
math.randomseed(now)
local limit_num = math.random(3, 50)
redis.call('setnx', user_limit_key, limit_num)
redis.call('expire', user_limit_key, 3600);

-- 限定抢宝活动时间
-- if (now <= start_ts + activity_time) then
    -- if (redis.call('exists', 'jbp:seizeYb:uid:' .. uid) == 1) then
        -- 如果数量未超过阀值，允许用户在限定时间内重复夺取
        if (tonumber(redis.call('get', issue_num_key)) >= yb_total_num) then
            -- 如果用户今天抢到超过他的随机数
            -- if (tonumber(redis.call('get', 'jbp:uid:' .. uid .. ':yb:num:' .. today)) >= tonumber(redis.call('get', user_limit_key))) then
                return 1;
            -- end
        end
    -- end
    -- redis.call('setnx', 'jbp:seizeYb:uid:' .. uid, 'Y');
    -- redis.call('expire', 'jbp:seizeYb:uid:' .. uid, activity_time);

    -- 限定时间内保证人手一个金元宝
    return 0;
-- end

-- 超过限定时间，活动结束
-- return 1;
LUA;

    //奖励层级
    private $reward_count = 2;
    private $reward_step = [0, 3, 5];

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($uid, $id, $config)
    {
        $this->uid = $uid;
        $this->id = $id;
        $this->config = $config;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // 队列用户
        $uid = $this->uid;

        // 每晚8点活动开始，判断是否开始
        $start_ts = $this->config['begin_time'];

        // 获取Redis实例
        $redis = $this->getRedis();

        // 每次点击都扣除用户金币
        if (UserCoinLog::totalJb($uid) > 0) {
            $this->updateUserJB($uid, -1);
        }

        if (!empty($this->id)) {
            // 元宝记录
            $yb_pending = DB::table('game_jbp_user_yb_pending')->where(['id' => $this->id, 'status' => 0])->first();

            $chest = 'Y';

            // 限定元宝领取条件
            $now = time();
            $today = date('Ymd');
            $params = [$uid, $start_ts, $now, $today, $this->config['yb_total_num'], $this->config['activity_time']];
            $lua_ret = $redis->eval(self::lua, $params);
            if ($lua_ret == 0) {
            } else if ($lua_ret == 1) {
                $chest = 'N';
            } else {
                $last_error = $redis->getLastError();
                Log::error("聚宝盆限定金元宝领取条件异常：" . json_encode($last_error));
                $chest = 'N';
            }

            if ($chest == 'Y') {
                if (UserCoinLog::totalXyb($uid) > 0) {
                    $data = ['ybid' => Uuid::uuid1(), 'uid' => $uid, 'type' => $yb_pending->type];
                    // 事务
                    DB::transaction(function () use ($redis, $uid, $data, $today) {
                        // 更新用户小妖币
                        $this->updateUserXYB($uid, -self::yb_cost_xyb);
                        // 新增用户金元宝
                        DB::table('game_jbp_user_yb')->insert($data);
                        // 统计当日盈亏
                        $this->statDayProfitAndLoss(self::yb_cost_xyb);
                        // 统计当日金元宝发放数量
                        $this->statDayOfferYbNum();
                        // 记录用户今日领取数量，过期时间1小时
                        $redis->incrBy("jbp:uid:$uid:yb:num:$today", 1);
                        $redis->setTimeout("jbp:uid:$uid:yb:num:$today", 60 * 60);
                        // 推荐奖励
                        try {
                            $this->rewardFinder(User::find($uid), $this->id);
                        } catch (\Exception $e) {
                        }
                        DB::table('game_jbp_user_yb_pending')->where(['id' => $this->id, 'status' => 0])->update(['status' => '1']);
                    });
                } else {
                    DB::table('game_jbp_user_yb_pending')->where(['id' => $this->id, 'status' => 0])->update(['status' => '2']);
                }
            } else {
                DB::table('game_jbp_user_yb_pending')->where(['id' => $this->id, 'status' => 0])->update(['status' => '2']);
            }
        }
    }

    public function failed()
    {
        if (!empty($this->id)) {
            DB::table('game_jbp_user_yb_pending')->where(['id' => $this->id, 'status' => 0])->update(['status' => '2']);
        }
    }

    /**
     * 获取Redis实例
     *
     * @return [type] [description]
     */
    private function getRedis() {
        static $redis = null;
        if (empty($redis)) {
            $redis = new \Redis();
            $redis->connect(env('REDIS_HOST'), env('REDIS_PORT'));
            $redis->auth(env('REDIS_PASSWORD'));
        }
        return $redis;
    }

    /**
     * 更新用户小妖币
     *
     * @param  [type] $uid  [description]
     * @param  [type] $gold [description]
     * @return [type]       [description]
     */
    private function updateUserXYB($uid, $gold) {
        $key = [
            'business_id' => self::business_id,
            'uid' => $uid,
            'xyb' => $gold,
            'ts' => time(),
        ];
        $key = json_encode($key);
        $tips = $gold > 0 ? '聚宝盆赢得小妖币' : '聚宝盆消耗小妖币';
        UserCoinLog::log(self::business_id, $uid, UserCoinLog::TYPE_XYB, $gold, $tips, $key);
    }

    /**
     * 更新用户小妖币
     *
     * @param  [type] $uid  [description]
     * @param  [type] $gold [description]
     * @return [type]       [description]
     */
    private function updateUserJB($uid, $gold) {
        $key = [
            'business_id' => self::business_id,
            'uid' => $uid,
            'jb' => $gold,
            'ts' => microtime(true),
        ];
        $key = json_encode($key);
        UserCoinLog::log(self::business_id, $uid, UserCoinLog::TYPE_JB, $gold, '聚宝盆消耗金币', $key);
    }

    /**
     * 奖励上级
     */
    private function rewardFinder($user, $id) {
        if ($this->reward_count) {
            if ($finder = User::where('id', $user->finder_id)->first()) {
                if ($xyb = $this->reward_step[$this->reward_count]) {
                    $tmp_xyb = $xyb / 100;
                    $message = '恭喜您获得' . $xyb . '个小妖币的推荐奖励。';
                    $key = '9-' . $id . '-' . $finder->id . '-' . $user->id . '-' . $tmp_xyb . '-' . time() . '-14';
                    UserCoinLog::log(9, $finder->id, UserCoinLog::TYPE_XYB, $tmp_xyb, '推荐奖励', $key);

                    dispatch(new SendWechatMessage($finder->openid, $message));

                    $this->reward_count--;
                    $this->rewardFinder($finder, $id);
                }
            }
        }
    }

    /**
     * 统计当日盈利情况（记录保留7天）
     *
     * @param  [type] $gold [description]
     * @return [type]       [description]
     */
    private function statDayProfitAndLoss($gold) {
        $redis = $this->getRedis();
        $day_stat_key = sprintf("jbp:stat:profit:%s", date('Ymd', time()));
        $redis->incrByFloat($day_stat_key, $gold);
        $redis->setTimeout($day_stat_key, 7*24*60*60);
    }

    /**
     * 统计当日发放数量（记录保留7天）
     *
     * @param  [type] $gold [description]
     * @return [type]       [description]
     */
    private function statDayOfferYbNum() {
        $redis = $this->getRedis();
        $day_stat_key = sprintf("jbp:stat:yb:issue:num:%s", date('Ymd', time()));
        $redis->incrBy($day_stat_key, 1);
        $redis->setTimeout($day_stat_key, 7*24*60*60);
    }
}
